<?php
/*
 * XenonPHP Tiny MVC Controller, by Olivier St-Laurent
 * Version 1.1
 *
 */

abstract class XenonController
{
    public $database = null;
    public $i18n = null;
    public $action = null;
    public $params = array();
    public $layout = "default";
    public $layoutDir;
    public $view;
    public $viewDir;
    public $USER = null;

    public function __construct($defaultAction = null, $params = array())
    {
        $this->layoutDir = APPLICATION_ROOT . "/layouts/";
        $this->viewDir = APPLICATION_ROOT . "/views/" . CONTROLLER . "/";
        if (empty($defaultAction)) {
            $defaultAction = 'index';
        }
        $this->action = $defaultAction;
        $this->params = $params;
        $this->view = new View($this);
    }

    public function __destruct()
    {
        if ($this->databaseConnected()) {
            $this->database->close();
        }
    }

    public function initDatabase($loginInfo = null)
    {
        X_mysql::$DefaultConnection = new X_mysql($loginInfo);
        $this->database = X_mysql::$DefaultConnection;
        if (function_exists('configureTables') && $this->database->connected) {
            configureTables($this->database);
        }
    }

    public function databaseConnected()
    {
        if (!empty($this->database) && is_object($this->database) && method_exists($this->database, 'isConnected')) {
            return $this->database->isConnected();
        }
        return false;
    }

    public function initI18n(&$getLang = null, $database = true)
    {
        $this->i18n = new i18n($getLang, ($database === true ? $this->database : $database), true);
    }

    public function init()
    {

    }

    public function loginMandatory($rememberRequest = true)
    {
        if (!$this->login()) {
            if ($rememberRequest) {
                $_SESSION['LAST_REQUEST_URI'] = $_SERVER['REQUEST_URI'];
            }
            $_SESSION['login'] = null;
            $this->redirect("login");
        }
    }

////////////////////////////////////////////////////////////////////////////////
    // Methods to Override in BaseController

    public function setLoginInfo($user)
    {
        $_SESSION['login']['username'] = $user->username;
        $_SESSION['login']['password'] = $user->password;
        $_SESSION['login']['name'] = $user->name;
        $_SESSION['login']['id'] = $user->id;
        setcookie('username', $user->username, time() + 30 * 24 * 3600, '/');
        setcookie('password', $user->password, time() + 30 * 24 * 3600, '/');
        $this->USER = $user;
    }

    public function logout()
    {
        setcookie('username', null, 1, '/');
        setcookie('password', null, 1, '/');
        unset($_SESSION['login']);
    }

    public function getLoginInfo(&$_username, &$_password)
    {
        if (!empty($_SESSION['login'])) {
            $_username = $_SESSION['login']['username'];
            $_password = $_SESSION['login']['password'];
        } else {
            if (!empty($_COOKIE['username'])) {
                $_username = $_COOKIE['username'];
                $_password = $_COOKIE['password'];
            }
        }
        return (!empty($_username) && !empty($_password));
    }

    public function errorBadPassword()
    {

    }

    public function errorEmptyPassword()
    {

    }

    public function loginAdditionalCheck(&$user)
    {
        return true;
    }

////////////////////////////////////////////////////////////////////////////////

    public function login(&$_username = null, &$_password = null, &$userByReference = null)
    {
        if (empty($_username)) {
            if (!$this->getLoginInfo($username, $password)) {
                return null;
            }
        } else {
            if (empty($_password)) {
                $this->errorEmptyPassword();
                return false;
            } else {
                $username = $_username;
                $password = $_password;
            }
        }
        if (!empty($username) && !empty($password)) {
            if ($this->databaseConnected()) {
                $m_users = new userModel();
                if (($user = $m_users->login($username, $password)) && $this->loginAdditionalCheck($user)) {
                    $this->setLoginInfo($user);
                    $userByReference = $user;
                    return true;
                } else {
                    if (!empty($_username)) {
                        $this->errorBadPassword();
                        return false;
                    }
                }
            }
        }
        return null;
    }

    public function redirect($url)
    {
        header("Location: " . $url);
        exit;
    }

    public function forward($action = 'index', $args_ = null)
    {
        if (is_array($args_)) {
            $args = $args_;
        } else {
            $args = func_get_args();
            array_shift($args);
        }
        $this->setAction($action);
        return call_user_func_array(array($this, $action . 'Action'), $args);
    }

    public function redirectAction($url = null, $action = 'index', $args_ = null)
    {
        if (is_array($args_)) {
            $args = $args_;
        } else {
            $args = func_get_args();
            array_shift($args);
            array_shift($args);
        }
        $this->view->clearHeadscripts();
        $this->setAction($action);
        call_user_func(array($this, 'init'));
        $this->setHeaderURL($url);
        return call_user_func_array(array($this, $action . 'Action'), $args);
    }

    public function forwardElement($element)
    {
        echo $this->view->renderElement($element);
        exit;
    }

    protected function issetPost($args_ = null)
    {
        foreach (func_get_args() as $arg) {
            if (!isset($_POST[$arg])) {
                return false;
            }
        }
        return true;
    }

    protected function notEmptyPost($args_ = null)
    {
        foreach (func_get_args() as $arg) {
            if (empty($_POST[$arg])) {
                return false;
            }
        }
        return true;
    }

    protected function getPost($param, $default = null)
    {
        if (isset($_POST[$param])) {
            return $_POST[$param];
        }
        return $default;
    }

    protected function getPosts()
    {
        return $_POST;
    }

    protected function get($param, $default = null)
    {
        if (isset($_GET[$param])) {
            return $_GET[$param];
        }
        return $default;
    }

    protected function getParam($i, $default = null)
    {
        if (isset($this->params[$i])) {
            return $this->params[$i];
        }
        return $default;
    }

    protected function getParams()
    {
        return $this->params;
    }

    /*
     * setPostFields(ModelRowReference, fields...);
     *
     * First param is a reference to the Row Object from the Model
     * Other params are the fields, considering that the Post Key is the same as the Database Field.
     * If second param is an array, this array will be the fields in that format : array(PostKey => FieldName, ...)
     *      If not an associative array, we consider that the Post Key is the same as the Database Field.
     *
     * To save post fields automatically :
     *      savePostFields(ModelRowReference, fields...)
     */

    public function setPostFields(&$row, $fields_ = null)
    {
        if (is_array($fields_)) {
            $fields = $fields_;
        } else {
            $fields = func_get_args();
            array_shift($fields);
        }
        foreach ($fields as $postKey => $field) {
            if (is_int($postKey))
                $postKey = $field;
            if ($this->issetPost($postKey)) {
                $row->$field = $this->getPost($postKey);
            }
        }
    }

    public function savePostFields(&$row, $fields_ = null)
    {
        call_user_func_array(array($this, 'setPostFields'), func_get_args());
        $row->save();
    }

    public function setAction($action)
    {
        $this->action = $action;
    }

    public function setLayout($layout)
    {
        $this->layout = $layout;
    }

    public function setLayoutDirectory($dir = null)
    {
        $this->layoutDir = $dir;
    }

    public function setViewDirectory($dir = null, $dir_ = null)
    {
        $this->viewDir = is_array($dir) ? $dir : func_get_args();
    }

    public function setHeaderURL($url = null)
    {
        header("X-FinalURL: " . (preg_match("#^/#", $url) ? root_url($url) : $url));
    }

}

class XenonView
{
    protected $headscripts = array();
    protected $controller;
    protected $javascript = "";
    protected static $currentViewInstance = null;

    public function __construct($controller)
    {
        $this->controller = $controller;
        self::$currentViewInstance = $this;
    }

    public function __set($name, $value)
    {
        $this->$name = $value;
    }

    public function __get($name)
    {
        if (!empty($this->$name)) {
            return $this->$name;
        }
        return null;
    }

    public static function __callStatic($name, $arguments)
    {
        return call_user_func_array([self::$currentViewInstance, $name], $arguments);
    }

    public function __call($name, $arguments)
    {
        if (!function_exists($name)) {
            $filenames = array();
            if (MODULE) {
                $filenames[] = APPLICATION_ROOT . "/helpers/" . MODULE . '/' . CONTROLLER . '/' . ACTION . '/' . $name . ".php";
                $filenames[] = APPLICATION_ROOT . "/helpers/" . MODULE . '/' . CONTROLLER . '/' . $name . ".php";
                $filenames[] = APPLICATION_ROOT . "/helpers/" . MODULE . '/' . $name . ".php";
            }
            $filenames[] = APPLICATION_ROOT . "/helpers/" . CONTROLLER . '/' . ACTION . '/' . $name . ".php";
            $filenames[] = APPLICATION_ROOT . "/helpers/" . CONTROLLER . '/' . $name . ".php";
            $filenames[] = APPLICATION_ROOT . "/helpers/" . $name . ".php";
            foreach ($filenames as $filename) {
                if (is_file($filename)) {
                    include_once $filename;
                    break;
                }
            }
        }
        if (is_callable($name)) {
            return call_user_func_array($name, $arguments);
        }
    }

    public function render($file)
    {
        if (is_file(APPLICATION_ROOT . "/views/" . $file)) {
            include APPLICATION_ROOT . "/views/" . $file;
        }
    }

    public function renderView($view = null)
    {
        if ($view === null) {
            $view = $this->controller->action;
        }
        $viewDir = $this->controller->viewDir;
        if (!is_array($viewDir)) {
            $viewDir = array($viewDir);
        }
        foreach ($viewDir as $viewDir) {
            if (is_file($viewDir . $view . ".phtml")) {
                include $viewDir . $view . ".phtml";
                return;
            }
        }
    }

    public function renderLayout($layout)
    {
        if (!empty($layout) && is_file($this->controller->layoutDir . $layout . ".phtml")) {
            include $this->controller->layoutDir . $layout . ".phtml";
        } else {
            $this->renderView();
        }
    }

    public function renderElement($element)
    {
        ob_start();
        foreach (array(
        APPLICATION_ROOT . '/elements/' . (MODULE ? MODULE . '/' : '') . str_replace('..', '', $element) . '.phtml',
        APPLICATION_ROOT . '/views/elements/' . (MODULE ? MODULE . '/' : '') . str_replace('..', '', $element) . '.phtml',
        APPLICATION_ROOT . '/elements/' . str_replace('..', '', $element) . '.phtml',
        APPLICATION_ROOT . '/views/elements/' . str_replace('..', '', $element) . '.phtml',
        ) as $file) {
            if ($file && is_file($file)) {
                include $file;
                break;
            }
        }
        return ob_get_clean();
    }

    public function renderHeadscripts($key = null)
    {
        $headscripts = $key === null ? $this->headscripts : (!empty($this->headscripts[$key]) ? array($this->headscripts[$key]) : array());
        ksort($headscripts);
        foreach ($headscripts as $heads) {
            foreach ($heads as $html) {
                echo "\n" . $html . "\n";
            }
        }
    }

    public function addHeadscript($name, $content, $key = 0)
    {
        $this->headscripts[$key][$name] = $content;
    }

    public function removeHeadscript($name, $key = 0)
    {
        unset($this->headscripts[$key][$name]);
    }

    public function clearHeadscripts()
    {
        $this->headscripts = array();
    }

    public function linkJS($filename, $key = 0)
    {
        $this->addHeadscript($filename, '<script type="text/javascript" src="' . (preg_match('#^https?://#', $filename) ? $filename : root_url("/js/" . $filename)) . '"></script>', $key);
    }

    public function linkCSS($filename, $key = 0)
    {
        $this->addHeadscript($filename, '<link type="text/css" rel="stylesheet" href="' . (preg_match('#^https?://#', $filename) ? $filename : root_url("/css/" . $filename)) . '" />', $key);
    }

    public function includeJS($filename, $key = 0)
    {
        ob_start();
        if (include_existing(
                (MODULE) ? DOCUMENT_ROOT . "/js/" . MODULE . '/' . $filename : null, DOCUMENT_ROOT . "/js/" . $filename, APPLICATION_ROOT . "/xenonlibs/js/" . $filename
            ))
            $this->addHeadscript($filename, '<script type="text/javascript">' . "\n" .
                ob_get_clean()
                . "\n" . '</script>', $key);
        else
            ob_end_flush();
    }

    public function includeCSS($filename, $key = 0)
    {
        ob_start();
        if (include_existing(
                (MODULE) ? DOCUMENT_ROOT . "/css/" . MODULE . '/' . $filename : null, DOCUMENT_ROOT . "/css/" . $filename, APPLICATION_ROOT . "/xenonlibs/css/" . $filename
            ))
            $this->addHeadscript($filename, '<style type="text/css">' . "\n" .
                preg_replace("#url\((['\"]?)(/?)([a-z0-9/\._-]+\.[a-z0-9]{2,5})(['\"]?)\)#i", "url('" . ROOT_URL . "/$3')", ob_get_clean())
                . "\n" . '</style>', $key);
        else
            ob_end_flush();
    }

    public function includePlugin($dirname, $key = 0)
    {
        foreach (array(
        (MODULE) ? DOCUMENT_ROOT . "/plugins/" . MODULE . '/' . $dirname : null,
        DOCUMENT_ROOT . "/plugins/" . $dirname,
        APPLICATION_ROOT . "/xenonlibs/plugins/" . $dirname
        ) as $dir) {
            if ($dir && is_dir($dir)) {
                if (is_file($file = $dir . '/style.css')) {
                    ob_start();
                    include $file;
                    $this->addHeadscript($file, '<style type="text/css">' . "\n" .
                        preg_replace("#url\((['\"]?)(/?)([a-z0-9/\._-]+\.[a-z0-9]{2,5})(['\"]?)\)#i", "url('" . ROOT_URL . preg_replace("#^.*(/plugins/.+)$#", "$1", $dir) . "/$3')", ob_get_clean())
                        . "\n" . '</style>', $key);
                }
                if (is_file($file = $dir . '/script.js')) {
                    ob_start();
                    include $file;
                    $this->addHeadscript($file, '<script type="text/javascript">' . "\n" .
                        ob_get_clean()
                        . "\n" . '</script>', $key);
                }
                return true;
            }
        }
        return false;
    }

}
